package manager;

import helper.PredictionResult;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import plotter.GraphPlotter;
import plotter.GraphPlotterImpl;
import plotter.GraphPlotterInfo;
import processor.PatientRecordProcessor;
import processor.Result;
import processor.RiskFactor;
import processor.RuleProcessor;
import util.AppUtil;
import bean.PatientGeneralInfo;
import bean.rule.RuleCondition;
import bean.rule.RuleInformation;

import comparator.CriticalityComparator;

public class PredictionManager {

	private PatientManager patientManager = new PatientManager();
	private RuleManager ruleManager = new RuleManager();
	
	List<RiskFactor> lstRiskFactors = new ArrayList<RiskFactor>();
	
	List<RiskFactor> lstOtherFactors = new ArrayList<RiskFactor>();
	
	Set<String> recommendedTests = new HashSet<String>();
	
	Set<String> resultsNotAvailable = new HashSet<String>();
	
	public PredictionManager(){
		
	}
	
	public PredictionResult predictDisease(String disease, long patientId){
		
		System.out.println("Fetching patient record.....");
		//Fetch patient record
		PatientGeneralInfo generalInfo = patientManager.getPatientDetails(patientId);
		
		System.out.println("Fetching rules.....");
		
		//Fetch rule conditions for a disease
		RuleInformation ruleInfo = ruleManager.getRuleForDisease(disease);
		

		Set<RuleCondition> ruleConditions = ruleInfo.getRuleConditions();
		
		//Sort the rule conditions based on the criticality of parameters
		TreeSet<RuleCondition> ruleConditions2 = new TreeSet<RuleCondition>(new CriticalityComparator());
		
		for(RuleCondition c : ruleConditions){
			ruleConditions2.add(c);
		}
		
		//Top 2 parameters --  change this for performance reasons
		String topParams = ((RuleCondition)ruleConditions2.toArray()[0]).getParameter() +","+((RuleCondition)ruleConditions2.toArray()[1]).getParameter();
		
		//Process the patient record
		PatientRecordProcessor recordProcessor = new PatientRecordProcessor(generalInfo);
		
		RuleProcessor ruleProcessor = new RuleProcessor(ruleManager.getRuleParameters());
		
		int totalCriticality = this.calculateTotalCriticality(ruleConditions);
						
		double count=0;
		
		List<GraphPlotterInfo> lstGPObjects = new ArrayList<GraphPlotterInfo>();
		
		for(RuleCondition condition : ruleConditions2){
			System.out.println("--------------------------------------------");
			String parameter = condition.getParameter();
			System.out.println("Parameter:"+parameter); 
			Object actualValue = recordProcessor.getRequestedPatientRecord(parameter);
			
			RiskFactor rf  = ruleProcessor.checkRuleCondition(condition,  actualValue);
			if(rf.getResult() == Result.TRUE){
				System.out.println("True");
				int criticality = condition.getCriticality();
				count += criticality + rf.getDeviationFromThreshold();
				lstRiskFactors.add(rf);			
				
			}	else		
			if(rf.getResult() == Result.NOT_AVAILABLE){
				System.out.println("NA");
				String source =  ruleProcessor.getParameterSource(parameter);
				if(source!=null){
					recommendedTests.add(source);
					resultsNotAvailable.add(rf.getRiskName());
				}
				rf.setCriticality(condition.getCriticality());
				lstOtherFactors.add(rf);				
			}
			
			if(topParams.contains(parameter)){
				GraphPlotterInfo info = new GraphPlotterInfo(rf, condition, ruleProcessor.getRuleParameter(parameter), 
						recordProcessor.getRequestedPatientRecord(parameter).toString());
				lstGPObjects.add(info);				
			}
		}
		
		double probability  = (count/totalCriticality);
		
		for(RiskFactor rf: lstOtherFactors){
			double newProb = (count+rf.getCriticality())/(totalCriticality);
			double pct = (newProb - probability)/probability;
			rf.setProbabilityChange( AppUtil.formatDecimal(pct));
		}
			
		
		GraphPlotter gpl = new GraphPlotterImpl(lstGPObjects, count, totalCriticality);
		
		
		PredictionResult prediction = new PredictionResult(disease);
		prediction.setProbability( AppUtil.formatDecimal(probability));
		prediction.setRiskFactors(lstRiskFactors);
		prediction.setOtherRiskFactors(lstOtherFactors);
		prediction.setTestsRecommended(recommendedTests);
		prediction.setResultsNotAvailable(resultsNotAvailable);
		prediction.setGraphObjects(gpl.getGraphPoints());
		
		System.out.println(probability + " " + count + " " + totalCriticality);
		
		return prediction;
		
	}

	private int calculateTotalCriticality(Set<RuleCondition> ruleConditions){
		int total = 0;
		for(RuleCondition rc : ruleConditions){
			total+=rc.getCriticality();
		}
		return total;
	}
	
	
	public static void main(String[] args){
		new PredictionManager().predictDisease("Heart Attack", 2);
	}
	
	
	
}
